package util

import (
	"errors"
	"fmt"
	"log"
	"net"
	"runtime"
	"strings"
	"time"
)

func FindIpv4Addr(addrs []net.Addr) (net.IP, error) {
	var ipv4Addr net.IP
	for _, addr := range addrs { // get ipv4 address
		if ipv4Addr = addr.(*net.IPNet).IP.To4(); ipv4Addr != nil {
			break
		}
	}

	if ipv4Addr == nil {
		return nil, errors.New("local machine doesn't have an ipv4 address")
	}

	return ipv4Addr, nil
}

func GetInterfaceIpv4Addr(interfaceName string) (addr string, err error) {
	var (
		ief      *net.Interface
		addrs    []net.Addr
		ipv4Addr net.IP
	)

	if ief, err = net.InterfaceByName(interfaceName); err != nil { // get interface
		return
	}
	if addrs, err = ief.Addrs(); err != nil { // get addresses
		return "", errors.New(fmt.Sprint(err))
	}

	ipv4Addr, err = FindIpv4Addr(addrs)
	if err != nil {
		return "", errors.New(fmt.Sprint(err))
	}

	return ipv4Addr.String(), nil
}

func GetAllInterfaces() ([]net.Addr, error) {
	addrs, err := net.InterfaceAddrs()

	if err != nil {
		return nil, errors.New(fmt.Sprint(err))
	}

	return addrs, nil

}

func GetLocalIp() string {
	osType := strings.ToLower(runtime.GOOS)

	if osType == "linux" {
		ip, err := GetInterfaceIpv4Addr("eth0")
		if err != nil {
			log.Fatalln(err)
		}
		return ip
	}

	if osType == "windows" {
		addr, err := GetAllInterfaces()
		if err != nil {
			log.Fatalln(err)
		}

		ipV4Addr, err := FindIpv4Addr(addr)

		if err != nil {
			log.Fatalln(err)
		}

		return ipV4Addr.String()
	}

	return ""
}

func IsOpened(host string, port string) bool {
	conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), time.Second*3)
	if err != nil {
		return false
	}

	if conn != nil {
		defer func(conn net.Conn) {
			_ = conn.Close()
		}(conn)
	}

	return true
}
